﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace TreeView
{
    [TemplatePart(Name = "ElementTimer", Type = typeof(Storyboard)), 
    TemplatePart(Name = "ElementChildren", Type = typeof(Canvas)), 
    TemplatePart(Name = "ElementContainer", Type = typeof(ScrollViewer))]
    public class TreeView : ExtendedControl
    {
        // Fields
        private Brush _backgroundBrush = new SolidColorBrush(Color.FromArgb(0xff, 0xff, 0xff, 0xff));
        private Point _drag = new Point();
        private TreeViewNode _draggedNode;
        private Point _dragStart = new Point();
        private int _dragStartIndex;
        private bool _enableCheckboxes;
        private int _framesSinceLastMouseMove;
        private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
        private Point _lastMousePosition = new Point();
        private TreeViewNode _mousedOver;
        private int _numberOfTicksToExpandOnHover = 30;
        private TreeViewNode _selected;
        private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;        
        //RWB start
        public ObservableCollection<TreeViewNode> SelectedItems { get; set; }
        private bool _multSelect = false;
        public bool MultiSelect
        {
            get { return _multSelect; }
            set { _multSelect = value; }
        }
        //RWB end

        internal const string ElementChildrenName = "ElementChildren";
        internal const string ElementContainerName = "ElementContainer";
        internal const string ElementTimerName = "ElementTimer";

        // Events
        public event TreeEventHandler Drag;

        public event TreeEventHandler Drop;

        public event TreeEventHandler TreeViewNodeCheckChanged;

        public event TreeEventHandler TreeViewNodeClick;

        public event TreeEventHandler TreeViewNodeCreated;

        public event TreeEventHandler TreeViewNodeStyleChanged;

        public event TreeEventHandler Populate;        
        //RWB start
        public event EventHandler TreeViewNodeDoubleClick;

        public event EventHandler SelectionChanged;
        //RWB end

        // Methods
        public TreeView()
        {
            this.TreeViewNodes = new ObservableCollection<TreeViewNode>();
            this.ApplyCheckChangesToChildren = false;
            this.MaxTextLength = -1;
            this.TitlePostfix = "...";
            this.TreeViewNodeHeight = 16.0;
            this.NumberOfTicksToExpandOnHover = 30;
            this.EnableDragAndDrop = false;
            base.EnableBaseEvents = true;
            base.IsTabStop = true;
            this.Focusable = true;
            base.TabNavigation = KeyboardNavigationMode.Once;
            //RWB start
            this.SelectedItems = new ObservableCollection<TreeViewNode>();
            //RWB end
        }

        public void BuildRoot()
        {
            TreeViewEventArgs args = new TreeViewEventArgs();
            this.Clear();
            this.RaisePopulate(this, args);
        }

        public void BulkUpdateBegin()
        {
            this.DisableChildUpdates();
        }

        public void BulkUpdateEnd()
        {
            this.EnableChildUpdates();
            this.SyncChildren();
        }

        private void CalculateSize()
        {
            double num2 = 0.0;
            if (base.ElementRoot != null)
            {
                if (this.TreeViewNodes.Count > 0)
                {
                    num2 = (double)this.TreeViewNodes[0].GetValue(Canvas.TopProperty);
                }
                for (int i = 0; i < this.TreeViewNodes.Count; i++)
                {
                    this.TreeViewNodes[i].SetValue(Canvas.TopProperty, num2);
                    num2 += (double)this.TreeViewNodes[i].GetValue(FrameworkElement.HeightProperty);
                }
                Point rootDimensions = this.GetRootDimensions();
                this.ElementChildren.Width = rootDimensions.X;
                this.ElementChildren.Height = rootDimensions.Y + 1.0;
            }
        }

        private void CalculateSize(TreeViewNode node)
        {
            node = node.ParentNode;
            while (node != null)
            {
                node.CalculateSize();
                node = node.ParentNode;
            }
            this.CalculateSize();
        }

        private void CancelDragAndDrop(TreeViewNode node)
        {
            if (this.EnableDragAndDrop && (node != null))
            {
                this.ElementChildren.Children.Remove(node);
                node.ParentNode.SyncChildren();
            }
        }

        protected void ChildNode_NodeIconMouseEnter(object sender, EventArgs args)
        {
            this._mousedOver = (TreeViewNode)sender;
        }

        protected void ChildNode_NodeIconMouseLeave(object sender, EventArgs args)
        {
            this._mousedOver = null;
        }

        protected void ChildNode_TextMouseDown(object sender, EventArgs args)
        {
            TreeViewNode node = (TreeViewNode)sender;
            TreeViewEventArgs args2 = new TreeViewEventArgs(node.ID);
            args2.Source = node;
            args2.Target = this._selected;
            this.RaiseNodeClick(sender, args2);            
            if (!args2.Cancel)// && (this._selected != node))
            {
                //RWB start
                //if there is at least one currently selected item
                if (this._selected != null)
                {
                    if (!_multSelect || (_multSelect && Keyboard.Modifiers != ModifierKeys.Control && Keyboard.Modifiers != ModifierKeys.Shift))
                    {
                        //Clear the Selected List
                        ClearSelected();
                        
                        if (this._selected.Editable)
                        {
                            this._selected.Editable = false;
                        }
                    }
                }

                //now Add the Selected
                //To add the multiselect capability we need to see if any of the keyboard modifiers are being pressed
                if (_multSelect)
                {
                    //Check to see if the user was pressing the control button while clicking so add the node to the 
                    //selected nodes
                    if (Keyboard.Modifiers == ModifierKeys.Shift)
                    {
                        string prevSetString = _selected.GetSetSTring();
                        string currentSetString = node.GetSetSTring();
                        bool usePrevious = false;
                        if (prevSetString != currentSetString)
                        {
                            for (int i = 0; i < prevSetString.Length; i++)
                            {
                                // if the currently selected node is a parent of the clicked node
                                if (currentSetString.Length == i)
                                {
                                    usePrevious = true;
                                    break;
                                }

                                // if the clicked node is a parent of the currently selected node
                                else if (prevSetString[i] == currentSetString[i] && i == (prevSetString.Length - 1))
                                {
                                    usePrevious = false;
                                    break;
                                }
                                //if the currently selected comes before the clicked node in the tree
                                else if (prevSetString[i] < currentSetString[i])
                                {
                                    usePrevious = false;
                                    break;
                                }
                                //if the clicked node comes before the currently selected in the tree
                                else if (prevSetString[i] > currentSetString[i])
                                {
                                    usePrevious = true;
                                    break;
                                }
                                //if it is equal, then we go to the next letter in the setstring
                            }

                            TreeViewNode temp = _selected;
                            while (temp != node)
                            {
                                if (!this.SelectedItems.Contains(temp))
                                    this.SelectedItems.Add(temp);

                                temp.Selected = true;

                                if (usePrevious)
                                    if (temp.PreviousEx() != null)
                                        temp = temp.PreviousEx();
                                    else
                                        break;
                                else
                                    if (temp.NextEx()!= null)
                                        temp = temp.NextEx();
                                    else
                                        break;
                            }

                            //now temp is the node we clicked, so we need to make sure it is selected
                            if (temp != null && !this.SelectedItems.Contains(temp))
                            {
                                this.SelectedItems.Add(temp);
                                this._selected = temp;
                                this._selected.Selected = true;
                            }
                        }
                    }
                    else
                    {
                        if (!this.SelectedItems.Contains(node))
                        {
                            this.SelectedItems.Add(node);
                            this._selected = node;
                            this._selected.Selected = true;
                        }
                        else
                        {
                            this.SelectedItems.Remove(node);
                            node.Selected = false;
                            if (SelectedItems.Count == 0)
                            {
                                _selected = null;
                            }
                            else
                            {
                                _selected = this.SelectedItems[0];
                            }
                        }
                    }
                }
                else
                {
                    this._selected = node;
                    this._selected.Selected = true;
                }
                
                RaiseSelectionChanged(this, new EventArgs());
                //RWB end
            }
            
        }
        //RWB start
        private void ClearSelected()
        {
            //If we are not multi-selecting then we to what was already here
            this._selected.Selected = false;

            //Clear all the selections in case the user has previously selected items
            foreach (TreeViewNode nde in SelectedItems)
                nde.Selected = false;

            //Clear the list
            SelectedItems = new ObservableCollection<TreeViewNode>();
        }
        //RWB end

        public void Clear()
        {
            for (int i = 0; i < this.TreeViewNodes.Count; i++)
            {
                this.TreeViewNodes[i].TreeViewNodes.Clear();
                base.Canvas.Children.Remove(this.TreeViewNodes[i]);
            }
            this.TreeViewNodes.Clear();
            if (this.ElementChildren != null)
            {
                this.ElementChildren.Children.Clear();
            }
            this.CalculateSize();
        }

        public void CollapseAll()
        {
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                node.CollapseAll();
            }
        }

        public void DisableChildUpdates()
        {
            this.TreeViewNodes.CollectionChanged -= new NotifyCollectionChangedEventHandler(this.Node_CollectionChanged);
        }

        private void DragNode(TreeViewNode node, Point position)
        {
            if (node != null)
            {
                TreeViewEventArgs args = new TreeViewEventArgs(node.ID);
                if ((this.EnableDragAndDrop && (node != null)) && !this.TreeViewNodes.Contains(node))
                {
                    args.Source = node;
                    this.RaiseDrag(this, args);
                    if (!args.Cancel)
                    {
                        this._draggedNode = node;
                        node.SetOpacity(0.35);
                        Point nodeAbsolutePosition = this.GetNodeAbsolutePosition(this._draggedNode);
                        this._dragStart.X = (double)this._draggedNode.GetValue(Canvas.LeftProperty);
                        this._dragStart.Y = (double)this._draggedNode.GetValue(Canvas.TopProperty);
                        this._drag = position;
                        this._dragStartIndex = this._draggedNode.ParentNode.TreeViewNodes.IndexOf(this._draggedNode);
                        this._draggedNode.ParentNode.Canvas.Children.Remove(this._draggedNode);
                        this.ElementChildren.Children.Add(this._draggedNode);
                        this._draggedNode.SetValue(Canvas.LeftProperty, nodeAbsolutePosition.X);
                        this._draggedNode.SetValue(Canvas.TopProperty, nodeAbsolutePosition.Y);
                        base.CaptureMouse();
                    }
                }
            }
        }

        private void DropNode(TreeViewNode node, Point position)
        {
            if (node != null)
            {
                TreeViewEventArgs args = new TreeViewEventArgs(node.ID);
                double x = position.X;
                double y = position.Y;
                if (this.EnableDragAndDrop)
                {
                    TreeViewNode item = this.GetChildNodeFromAll(x + this.ElementContainer.HorizontalOffset, y + this.ElementContainer.VerticalOffset, this.TreeViewNodes, node);
                    node.SetOpacity(1.0);
                    if ((item != null) && (item != node))
                    {
                        args.Source = this._draggedNode;
                        args.Target = item;
                        this.RaiseDrop(this, args);
                        if (!args.Cancel)
                        {
                            if ((args.DropAction == DropActions.AppendAsLastChild) || (args.DropAction == DropActions.AppendAsFirstChild))
                            {
                                this.ElementChildren.Children.Remove(node);
                                this.CalculateSize(node);
                                TreeViewNode parentNode = node.ParentNode;
                                parentNode.TreeViewNodes.Remove(node);
                                if (args.DropAction == DropActions.AppendAsLastChild)
                                {
                                    item.TreeViewNodes.Add(node);
                                }
                                else
                                {
                                    item.TreeViewNodes.Insert(0, node);
                                }
                                item.UpdateVisuals();
                                node.Visibility = item.Expanded ? Visibility.Visible : Visibility.Collapsed;
                                if (parentNode != null)
                                {
                                    parentNode.UpdateChildCount();
                                }
                            }
                            else if ((args.DropAction == DropActions.InsertAfter) || (args.DropAction == DropActions.InsertBefore))
                            {
                                int index;
                                this.ElementChildren.Children.Remove(node);
                                node.ParentNode.TreeViewNodes.Remove(node);
                                if (item.ParentNode != null)
                                {
                                    index = item.ParentNode.TreeViewNodes.IndexOf(item);
                                    if (args.DropAction == DropActions.InsertAfter)
                                    {
                                        item.ParentNode.TreeViewNodes.Insert(index + 1, node);
                                    }
                                    else
                                    {
                                        item.ParentNode.TreeViewNodes.Insert(index, node);
                                    }
                                }
                                else
                                {
                                    index = this.TreeViewNodes.IndexOf(item);
                                    if (args.DropAction == DropActions.InsertAfter)
                                    {
                                        this.TreeViewNodes.Insert(index + 1, node);
                                    }
                                    else
                                    {
                                        this.TreeViewNodes.Insert(index, node);
                                    }
                                }
                                node.RecursiveUpdateSize();
                            }
                            else
                            {
                                this.CancelDragAndDrop(node);
                            }
                            this.CalculateSize(node);
                        }
                        else
                        {
                            this.CancelDragAndDrop(node);
                        }
                    }
                    else
                    {
                        this.CancelDragAndDrop(node);
                    }
                    base.ReleaseMouseCapture();
                }
            }
        }

        protected void ElementTimer_Completed(object sender, EventArgs e)
        {
            double x = this._lastMousePosition.X;
            double y = this._lastMousePosition.Y;
            if (this._draggedNode != null)
            {
                double scrollableHeight;
                double num4 = 10.0;
                if (y < 0.0)
                {
                    scrollableHeight = this.ElementContainer.VerticalOffset - num4;
                    if (scrollableHeight < 0.0)
                    {
                        scrollableHeight = 0.0;
                        num4 = 0.0;
                    }
                    this.ElementContainer.ScrollToVerticalOffset(scrollableHeight);
                    this.MoveNode(this._draggedNode, new Point(this._lastMousePosition.X, this._lastMousePosition.Y - num4));
                    this._drag.Y += num4;
                }
                else if (y > this.ElementContainer.Height)
                {
                    scrollableHeight = this.ElementContainer.VerticalOffset + num4;
                    if (scrollableHeight > this.ElementContainer.ScrollableHeight)
                    {
                        scrollableHeight = this.ElementContainer.ScrollableHeight;
                        num4 = 0.0;
                    }
                    this.ElementContainer.ScrollToVerticalOffset(scrollableHeight);
                    this.MoveNode(this._draggedNode, new Point(this._lastMousePosition.X, this._lastMousePosition.Y + num4));
                    this._drag.Y -= num4;
                }
                if (this._framesSinceLastMouseMove == this._numberOfTicksToExpandOnHover)
                {
                    TreeViewNode node = this.GetChildNodeFromAll(x + this.ElementContainer.HorizontalOffset, y + this.ElementContainer.VerticalOffset, this.TreeViewNodes, this._draggedNode);
                    if ((node != null) && !node.Expanded)
                    {
                        node.Expand();
                    }
                }
            }
            this._framesSinceLastMouseMove++;
            this.ElementTimer.Begin();
            this.ElementChildren.Height = this.ElementChildren.Height;
        }

        public void EnableChildUpdates()
        {
            this.TreeViewNodes.CollectionChanged -= new NotifyCollectionChangedEventHandler(this.Node_CollectionChanged);
            this.TreeViewNodes.CollectionChanged += new NotifyCollectionChangedEventHandler(this.Node_CollectionChanged);
        }

        public void ExpandAll()
        {
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                node.ExpandAll();
            }
        }

        public List<TreeViewNode> Find(string terms)
        {
            List<TreeViewNode> list = new List<TreeViewNode>();
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                list.AddRange(node.Find(terms));
            }
            return list;
        }

        public TreeViewNode Get(string id)
        {
            TreeViewNode node = null;
            foreach (TreeViewNode node2 in this.TreeViewNodes)
            {
                if (string.Compare(node2.ID, id, StringComparison.CurrentCulture) == 0)
                    return node2;

                node = node2.Get(id);
                if (node != null)
                {
                    return node;
                }
            }
            return node;
        }

        private TreeViewNode GetChildNode(double x, double y, TreeViewNode parent, TreeViewNode exclude)
        {
            Point point = new Point();
            TreeViewNode node = null;
            if (parent == null)
            {
                return null;
            }
            Point nodeAbsolutePosition = this.GetNodeAbsolutePosition(parent);
            if (((x >= nodeAbsolutePosition.X) && (x <= (nodeAbsolutePosition.X + parent.Width))) && ((y >= nodeAbsolutePosition.Y) && (y <= (nodeAbsolutePosition.Y + this.TreeViewNodeHeight))))
            {
                return parent;
            }
            foreach (TreeViewNode node2 in parent.TreeViewNodes)
            {
                if (node2 == exclude)
                {
                    continue;
                }
                point.X = ((double)node2.GetValue(Canvas.LeftProperty)) + nodeAbsolutePosition.X;
                point.Y = ((double)node2.GetValue(Canvas.TopProperty)) + nodeAbsolutePosition.Y;
                if (((x >= point.X) && (x <= (point.X + node2.Width))) && ((y >= point.Y) && (y <= (point.Y + this.TreeViewNodeHeight))))
                {
                    node = node2;
                }
                else if (node2.HasChildren && node2.Expanded)
                {
                    node = this.GetChildNode(x, y, node2, exclude);
                }
                if (node != null)
                {
                    return node;
                }
            }
            return node;
        }

        private TreeViewNode GetChildNodeFromAll(double x, double y, ObservableCollection<TreeViewNode> nodes, TreeViewNode exclude)
        {
            TreeViewNode node = null;
            foreach (TreeViewNode node2 in nodes)
            {
                node = this.GetChildNode(x, y, node2, exclude);
                if (node != null)
                {
                    return node;
                }
            }
            return node;
        }

        private Point GetNodeAbsolutePosition(TreeViewNode node)
        {
            Point point = new Point();
            while (node != null)
            {
                point.X += (double)node.GetValue(Canvas.LeftProperty);
                point.Y += (double)node.GetValue(Canvas.TopProperty);
                node = node.ParentNode;
            }
            return point;
        }

        private Point GetRootDimensions()
        {
            Point point = new Point();
            foreach (TreeViewNode node in this.TreeViewNodes)
            {
                if (node.Width > point.X)
                {
                    point.X = node.Width;
                }
                point.Y += node.Height;
            }
            return point;
        }

        private void MoveNode(TreeViewNode node, Point position)
        {
            if (node != null)
            {
                double x = position.X;
                double y = position.Y;
                this._draggedNode.AddPosition(x - this._drag.X, y - this._drag.Y);
                this._drag.X = x;
                this._drag.Y = y;
            }
        }

        private void Node_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.SyncChildren();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.ElementContainer = (ScrollViewer)base.GetTemplateChild("ElementContainer");
            this.ElementChildren = (Canvas)base.GetTemplateChild("ElementChildren");
            this.ElementTimer = (Storyboard)base.GetTemplateChild("ElementTimer");
            this.ElementTimer.Completed += new EventHandler(this.ElementTimer_Completed);
            this.ElementContainer.Padding = new Thickness(1.0);
            this.UpdateVisualState();
            this.SyncChildren();
            this.TreeViewNodes.CollectionChanged += new NotifyCollectionChangedEventHandler(this.Node_CollectionChanged);
        }

        protected override void OnKeyDown(object sender, KeyEventArgs e)
        {
            //RWB start - moved the check for EnableNodeEditing into the F2 section
            if ((base.IsEnabled && this.IsFocused) && ((this._selected != null) ))
            {
                switch(e.Key)
                {
                    case Key.F2:
                        if(this.EnableNodeEditing)
                            this._selected.Editable = !this._selected.Editable;
                        break;
                    default:
                        if (this._selected.Editable && (e.Key == Key.Enter || e.Key == Key.Escape))
                        {
                            this._selected.Editable = false;
                        }                        
                        break;
                }
            }            
            //RWB end
        }    
        //RWB start Added for the arrow keys - arrow keys do not fire OnKeyDownEvent
        protected override void OnKeyUp(object sender, KeyEventArgs e)
        {
            if ((base.IsEnabled && this.IsFocused) && this._selected != null && !this._selected.Editable)
            {
                switch (e.Key)
                {
                    case Key.Up:                        
                        ProcessArrow(_selected.PreviousEx());
                        break;
                    case Key.Down:
                        ProcessArrow(_selected.NextEx());
                        break;
                }
            }
        }

        private void ProcessArrow(TreeViewNode nde)
        {
            //if there is a node to go to
            if (nde != null)
            {
                //If the user isn't pressing shift then clear any currently selected nodes
                if (Keyboard.Modifiers != ModifierKeys.Shift || !_multSelect)
                    ClearSelected();

                //if it is already selected then the user has navigated back from where they came
                if (nde.Selected)
                {
                    //Remove where we came from
                    SelectedItems.Remove(_selected);
                    //Unhighlight the node
                    _selected.Selected = false;
                    //make this our new selected
                    _selected = nde;
                }
                else
                {
                    //Make this our newly selected node
                    _selected = nde;
                    //Highlight it
                    _selected.Selected = true;
                    //Add it to the selected nodes
                    SelectedItems.Add(nde);
                }

                //Tell the controll we've changed the selection
                RaiseSelectionChanged(this, new EventArgs());
            }
        }
        //RWB end

        protected override void OnMouseDown(object sender, MouseButtonEventArgs args)
        {
            this.DragNode(this._mousedOver, args.GetPosition(this));
        }

        protected override void OnMouseMove(object sender, MouseEventArgs args)
        {
            this._lastMousePosition = args.GetPosition(this);
            this.MoveNode(this._draggedNode, this._lastMousePosition);
            this._framesSinceLastMouseMove = 0;
        }

        protected override void OnMouseUp(object sender, MouseButtonEventArgs args)
        {
            this.DropNode(this._draggedNode, args.GetPosition(this));
            this._draggedNode = null;
        }

        private void RaiseDrag(object sender, TreeViewEventArgs args)
        {
            if (this.Drag != null)
            {
                this.Drag(sender, args);
            }
        }

        private void RaiseDrop(object sender, TreeViewEventArgs args)
        {
            if (this.Drop != null)
            {
                this.Drop(sender, args);
            }
        }

        private void RaiseNodeCheckChanged(object sender, TreeViewEventArgs args)
        {
            TreeViewNode node1 = (TreeViewNode)sender;
            if (this.TreeViewNodeCheckChanged != null)
            {
                this.TreeViewNodeCheckChanged(sender, args);
            }
        }

        private void RaiseNodeClick(object sender, TreeViewEventArgs args)
        {
            if (this.TreeViewNodeClick != null)
            {
                this.TreeViewNodeClick(sender, args);
            }
        }

        //RWB start
        private void RaiseDoubleClick(object sender, EventArgs args)
        {
            if (this.TreeViewNodeDoubleClick != null)
            {
                this.TreeViewNodeDoubleClick(sender, args);
            }
        }

        private void RaiseSelectionChanged(object sender, EventArgs args)
        {
            if (this.SelectionChanged != null)
            {
                this.SelectionChanged(sender, args);
            }
        }
        //RWB end

        private void RaiseNodeCreated(object sender, TreeViewEventArgs args)
        {
            TreeViewNode node1 = (TreeViewNode)sender;
            if (this.TreeViewNodeCreated != null)
            {
                this.TreeViewNodeCreated(sender, args);
            }
        }

        private void RaiseNodeStyleChanged(object sender, TreeViewEventArgs args)
        {
            TreeViewNode node1 = (TreeViewNode)sender;
            if (this.TreeViewNodeStyleChanged != null)
            {
                this.TreeViewNodeStyleChanged(sender, args);
            }
        }

        private void RaisePopulate(object sender, TreeViewEventArgs args)
        {
            if (this.Populate != null)
            {
                this.Populate(sender, args);
            }
        }

        public void Sort(SortActions sort)
        {
            bool sortAll = (sort & SortActions.ApplyToChildren) != SortActions.Default;
            if ((sort & SortActions.ContainersFirst) != SortActions.Default)
            {
                if ((sort & SortActions.Descending) != SortActions.Default)
                {
                    this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortReverseContainersFirstCallback));
                }
                else
                {
                    this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortContainersFirstCallback));
                }
            }
            else if ((sort & SortActions.Descending) != SortActions.Default)
            {
                this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortReverseCallback));
            }
            else
            {
                this.Sort(sortAll, new Comparison<TreeViewNode>(TreeViewNode.SortCallback));
            }
        }

        public void Sort(bool sortAll, Comparison<TreeViewNode> comparer)
        {
            List<TreeViewNode> list = new List<TreeViewNode>();
            list.AddRange(this.TreeViewNodes);
            list.Sort(comparer);
            this.BulkUpdateBegin();
            this.TreeViewNodes.Clear();
            foreach (TreeViewNode node in list)
            {
                this.TreeViewNodes.Add(node);
                if (sortAll)
                {
                    node.Sort(sortAll, comparer);
                }
            }
            this.BulkUpdateEnd();
        }

        private int SortCallback(TreeViewNode a, TreeViewNode b)
        {
            return a.Text.CompareTo(b.Text);
        }

        private int SortReverseCallback(TreeViewNode a, TreeViewNode b)
        {
            return b.Text.CompareTo(a.Text);
        }

        private void SyncChildren()
        {
            double num = 0.0;
            if (base.ElementRoot != null)
            {
                for (int i = this.ElementChildren.Children.Count - 1; i >= 0; i--)
                {
                    if (this.ElementChildren.Children[i] is TreeViewNode)
                    {
                        this.ElementChildren.Children.Remove(this.ElementChildren.Children[i]);
                    }
                }
                for (int j = 0; j < this.TreeViewNodes.Count; j++)
                {
                    this.TreeViewNodes[j].ParentNode = null;
                    this.TreeViewNodes[j].Base = this;
                    this.TreeViewNodes[j].OverallHeight = this.TreeViewNodeHeight;
                    this.TreeViewNodes[j].SetValue(Canvas.LeftProperty, 5.0);
                    this.TreeViewNodes[j].SetValue(Canvas.TopProperty, num);
                    this.TreeViewNodes[j].TreeViewNodeTextMouseDown -= new EventHandler(this.ChildNode_TextMouseDown);
                    this.TreeViewNodes[j].TreeViewNodeIconMouseEnter -= new EventHandler(this.ChildNode_NodeIconMouseEnter);
                    this.TreeViewNodes[j].TreeViewNodeIconMouseLeave -= new EventHandler(this.ChildNode_NodeIconMouseLeave);
                    this.TreeViewNodes[j].PopulateChildren -= new TreeEventHandler(this.RaisePopulate);
                    this.TreeViewNodes[j].TreeViewNodeCreated -= new TreeEventHandler(this.RaiseNodeCreated);
                    this.TreeViewNodes[j].NodeStyleChanged -= new TreeEventHandler(this.RaiseNodeStyleChanged);
                    this.TreeViewNodes[j].TreeViewNodeCheckChanged -= new TreeEventHandler(this.RaiseNodeCheckChanged);
                    this.TreeViewNodes[j].TreeViewNodeUpdateBase -= new EventHandler(this.Tree_NodeUpdateBase);                    
                    this.TreeViewNodes[j].TreeViewNodeTextMouseDown += new EventHandler(this.ChildNode_TextMouseDown);
                    this.TreeViewNodes[j].TreeViewNodeIconMouseEnter += new EventHandler(this.ChildNode_NodeIconMouseEnter);
                    this.TreeViewNodes[j].TreeViewNodeIconMouseLeave += new EventHandler(this.ChildNode_NodeIconMouseLeave);
                    this.TreeViewNodes[j].TreeViewNodeIconMouseDown += new MouseEventHandler(this.Tree_NodeIconMouseDown);
                    this.TreeViewNodes[j].PopulateChildren += new TreeEventHandler(this.RaisePopulate);
                    this.TreeViewNodes[j].TreeViewNodeCreated += new TreeEventHandler(this.RaiseNodeCreated);
                    this.TreeViewNodes[j].NodeStyleChanged += new TreeEventHandler(this.RaiseNodeStyleChanged);
                    this.TreeViewNodes[j].TreeViewNodeCheckChanged += new TreeEventHandler(this.RaiseNodeCheckChanged);
                    this.TreeViewNodes[j].TreeViewNodeUpdateBase += new EventHandler(this.Tree_NodeUpdateBase);
                    this.ElementChildren.Children.Add(this.TreeViewNodes[j]);
                    num += this.TreeViewNodes[j].Height;

                    //RWB start
                    this.TreeViewNodes[j].TreeViewNodeDoubleClick -= new TreeEventHandler(RaiseDoubleClick);
                    this.TreeViewNodes[j].TreeViewNodeDoubleClick += new TreeEventHandler(RaiseDoubleClick);
                    //RWB end
                }
                this.CalculateSize();
            }
        }

        private void Tree_NodeIconMouseDown(object sender, MouseEventArgs e)
        {
            this.DragNode((TreeViewNode)sender, e.GetPosition(this));
        }

        private void Tree_NodeUpdateBase(object sender, EventArgs e)
        {
            this.CalculateSize();
        }

        private void UpdateContainer()
        {
            if (base.ElementRoot != null)
            {
                this.ElementContainer.VerticalScrollBarVisibility = this._verticalScrollBarVisibility;
                this.ElementContainer.HorizontalScrollBarVisibility = this._horizontalScrollBarVisibility;
            }
        }

        protected override void UpdateVisualState()
        {
            if (base.ElementRoot != null)
            {
                this.ElementContainer.Width = base.Width - 2.0;
                this.ElementContainer.Height = base.Height - 2.0;
                this.ElementContainer.Background = this._backgroundBrush;
                this.UpdateContainer();
            }
            base.UpdateVisualState();
        }

        // Properties


        public bool ApplyCheckChangesToChildren { get; set; }


        public new Brush Background
        {
            get
            {
                return this.Fill;
            }
            set
            {
                this.Fill = value;
            }
        }

        internal Canvas ElementChildren { get; set; }


        internal ScrollViewer ElementContainer { get; set; }


        internal Storyboard ElementTimer { get; set; }


        public bool EnableCheckboxes
        {
            get
            {
                return this._enableCheckboxes;
            }
            set
            {
                this._enableCheckboxes = value;
                foreach (TreeViewNode node in this.TreeViewNodes)
                {
                    node.EnableCheckboxes = value;
                }
            }
        }

        public bool EnableDragAndDrop { get; set; }


        public bool EnableNodeEditing { get; set; }


        public Brush Fill
        {
            get
            {
                return this._backgroundBrush;
            }
            set
            {
                this._backgroundBrush = value;
                this.UpdateVisualState();
            }
        }

        public ScrollBarVisibility HorizontalScrollBarVisibility
        {
            get
            {
                return this._horizontalScrollBarVisibility;
            }
            set
            {
                this._horizontalScrollBarVisibility = value;
                this.UpdateContainer();
            }
        }

        public int MaxTextLength { get; set; }


        public double TreeViewNodeHeight { get; set; }


        public ObservableCollection<TreeViewNode> TreeViewNodes { get; set; }


        public int NumberOfTicksToExpandOnHover { get; set; }


        public ScrollViewer ScrollViewer
        {
            get
            {
                return this.ElementContainer;
            }
        }

        public TreeViewNode Selected
        {
            get
            {
                return this._selected;
            }
            set
            {
                if (this._selected != null)
                {
                    this._selected.Selected = false;
                }
                this._selected = value;
                this._selected.Selected = true;
                SelectedItems = new ObservableCollection<TreeViewNode>();
                SelectedItems.Add(value);
                RaiseSelectionChanged(this, new EventArgs());
            }
        }

        public string TitlePostfix { get; set; }


        public ScrollBarVisibility VerticalScrollBarVisibility
        {
            get
            {
                return this._verticalScrollBarVisibility;
            }
            set
            {
                this._verticalScrollBarVisibility = value;
                this.UpdateContainer();
            }
        }

        // Nested Types
        public enum DropActions
        {
            AppendAsFirstChild,
            AppendAsLastChild,
            InsertBefore,
            InsertAfter
        }

        public enum SortActions
        {
            ApplyToChildren = 4,
            ContainersFirst = 2,
            Default = 0,
            Descending = 1
        }
    }
}
